#pragma once

#include "CoreMinimal.h"
#include "Components/ShapeComponent.h"
#include "Components/AutoShape/AutoShapeConfigBase.h"
#include "AutoShapeConfigBox.generated.h"

/**
 * 
 */
UCLASS(meta = (DisplayName = "盒子碰撞"))
class BASECLASS_API UAutoShapeConfigBox : public UAutoShapeConfigBase
{
	GENERATED_BODY()

public:
	//~ Begin UPrimitiveComponent Interface.
	virtual FPrimitiveSceneProxy* CreateSceneProxy(UAutoShapeComponent* Owner) override;
	virtual bool IsZeroExtent(const UAutoShapeComponent* Owner) const override;
	virtual struct FCollisionShape GetCollisionShape(const UAutoShapeComponent* Owner, float Inflation = 0.0f) const override;
	//~ End UPrimitiveComponent Interface.

	//~ Begin USceneComponent Interface
	virtual FBoxSphereBounds CalcBounds(const UAutoShapeComponent* Owner, const FTransform& LocalToWorld) const override;
	//~ End USceneComponent Interface

	//~ Begin UShapeComponent Interface
	virtual void UpdateBodySetup(UAutoShapeComponent* Owner) override;
	//~ End UShapeComponent Interface

public:
	/**
	 * Change the box extent size. This is the unscaled size, before component scale is applied.
	 * @param	InBoxExtent: new extent (radius) for the box.
	 * @param	bUpdateOverlaps: if true and this shape is registered and collides, updates touching array for owner actor.
	 */
	UFUNCTION(BlueprintCallable, Category = "Components|Box")
	void SetBoxExtent(UAutoShapeComponent* Owner, FVector InBoxExtent, bool bUpdateOverlaps = true);

	// @return the box extent, scaled by the component scale.
	UFUNCTION(BlueprintCallable, Category = "Components|Box")
	FVector GetScaledBoxExtent(const USceneComponent* Owner) const { return Owner ? BoxExtent * Owner->GetComponentTransform().GetScale3D() : GetUnscaledBoxExtent(); }

	// @return the box extent, ignoring component scale.
	UFUNCTION(BlueprintCallable, Category = "Components|Box")
	FORCEINLINE FVector GetUnscaledBoxExtent() const { return BoxExtent; }

public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, export, Category = Shape)
	FVector BoxExtent = FVector(100.0f, 100.0f, 100.0f);
};

/** Represents a UBoxComponent to the scene manager. */
class FKBoxSceneProxy final : public FPrimitiveSceneProxy
{
public:
	virtual SIZE_T GetTypeHash() const override
	{
		static size_t UniquePointer;
		return reinterpret_cast<size_t>(&UniquePointer);
	}

	FKBoxSceneProxy(const UShapeComponent* InComponent, const UAutoShapeConfigBox* InConfig)
		: FPrimitiveSceneProxy(InComponent)
		, bDrawOnlyIfSelected(InComponent->bDrawOnlyIfSelected)
		, BoxExtents(InConfig->BoxExtent)
		, BoxColor(InComponent->ShapeColor)
	{
		bWillEverBeLit = false;
	}

	virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
	{
		QUICK_SCOPE_CYCLE_COUNTER(STAT_BoxSceneProxy_GetDynamicMeshElements);

		const FMatrix& InLocalToWorld = GetLocalToWorld();

		for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		{
			if (VisibilityMap & (1 << ViewIndex))
			{
				const FSceneView* View = Views[ViewIndex];

				const FLinearColor DrawColor = GetViewSelectionColor(BoxColor, *View, IsSelected(), IsHovered(), false, IsIndividuallySelected());

				FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
				DrawOrientedWireBox(PDI, InLocalToWorld.GetOrigin(), InLocalToWorld.GetScaledAxis(EAxis::X), InLocalToWorld.GetScaledAxis(EAxis::Y), InLocalToWorld.GetScaledAxis(EAxis::Z), BoxExtents, DrawColor, SDPG_World);
			}
		}
	}

	virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
	{
		const bool bProxyVisible = !bDrawOnlyIfSelected || IsSelected();

		// Should we draw this because collision drawing is enabled, and we have collision
		const bool bShowForCollision = View->Family->EngineShowFlags.Collision && IsCollisionEnabled();

		FPrimitiveViewRelevance Result;
		Result.bDrawRelevance = (IsShown(View) && bProxyVisible) || bShowForCollision;
		Result.bDynamicRelevance = true;
		Result.bShadowRelevance = IsShadowCast(View);
		Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
		return Result;
	}

	virtual uint32 GetMemoryFootprint(void) const override { return (sizeof(*this) + GetAllocatedSize()); }
	uint32 GetAllocatedSize(void) const { return (FPrimitiveSceneProxy::GetAllocatedSize()); }

private:
	const uint32 bDrawOnlyIfSelected : 1;
	const FVector BoxExtents;
	const FColor BoxColor;
};
